#!/usr/bin/env python
#=======================================================================
#
#          FILE: ibport.py
#         USAGE: ./ibport.py [port]
#   DESCRIPTION: Print InfiniBand port info (e.g., LID, VENDOR..)
#  REQUIREMENTS: ibnetdiscover
#          BUGS: N/A
#         NOTES: N/A
#        AUTHOR: Ali Ayoub
#         EMAIL: ali@mellanox.com
#       COMPANY: Mellanox
#       CREATED: 09.17.2010-16:09:30
#      REVISION: 1.20
#=======================================================================

import sys
import popen2
import time
import os

USAGE =  "Usage: %s [port]" % (os.path.basename(sys.argv[0]))

debug = "--debug" in sys.argv
if debug:
	sys.argv.remove("--debug")

log = "--log" in sys.argv
if log:
	sys.argv.remove("--log")

wait = "--wait" in sys.argv
if wait:
	sys.argv.remove("--wait")

def vprint(msg):
	if debug > 0:
		sys.stdout.write(str(msg) + '\n')
		if log > 0:
			fd = open(LOG, 'a')
			now =  time.strftime("%Y.%m.%d-%H.%M.%S", time.localtime())
			fd.write("%s:%s: %s\n" % (now, str(sys.argv), msg))
			fd.close()


def run_cmd(cmd, retry = 0, nap = 1):
	if wait: # if wait flag set, use longer nap
		nap = 10
	p = popen2.Popen4(cmd)
	rc = p.wait()
	if rc:
		out = "-E- FAILED"
	else:
		out = p.fromchild.read()
	vprint("-V- cmd [%s], rc [%s], out_len [%d], retry [%d]" % \
		(str(cmd), str(rc), len(out), retry))
	if (rc or len(out) == 0) and retry > 0:
		retry = retry -1
		try:
			time.sleep(nap)
		except:
			return (rc, out)
		return run_cmd(cmd, retry, nap)

	return (rc, out)

def get_type_desc(devid):
	if devid == None or len(devid) < 3:
		vprint("-V- cannot parse %s" % str(devid))
		return None

	pciid = "/usr/share/hwdata/pci.ids"
	if not os.path.exists(pciid):
		vprint("-V- %s not found" % pciid)
		return None

	devid = devid[2:]
	cmd = "grep %s %s |  awk '{print $2}'" % (devid, pciid)
	(rc, out) = run_cmd(cmd)
	if rc:
		vprint("-V- cmd failed %s" % cmd)
		return None

	if len(out.strip()) == 0:
		vprint("-V- cmd failed %s, no output" % cmd)
		return None
	
	return out.strip()

def get_type (Ca, Switch):
	type = "unknwon"
	tdesc = None

	if   Switch != None:
		type = "Switch"
	elif Ca != None:
		if Ca.count("BX"):
			type = "BridgeX"
		else:
			type = "CA"

	return type

def get_vendor(vendid):
	# http://support.systemfabricworks.com/lxr/#ofed+OFED-1.5/openmpi-1.4/ompi/mca/btl/openib/mca-btl-openib-device-params.ini
	if vendid in ["0x2c9"]:
		return "Mellanox"
	if vendid in ["0x5ad"]:
		return "Cisco"
	if vendid in ["0x66a"]:
		return "Silverstorm"
	if vendid in ["0x8f1"]:
		return "Voltaire"
	if vendid in ["0x5076"]:
		return "IBM"
	if vendid in ["0x1fc1"]:
		return "Qlogic"
	if vendid in ["0x1708"]:
		return "HP"
	if vendid in ["0x3ba"]:
		return "Sun"
	return str(vendid)


def get_lid(Ca, Switch, port_desc):
	if Ca != None:
		lid = port_desc.split("lid")[1].split()[0]
	else:
		lid = Switch.split("lid")[1].split()[0]
	return lid

def get_guid(Ca, Switch, port_desc, switchguid):
	if Ca != None:
		guid = port_desc.split("(")[1].split(")")[0]
	else:
		guid = switchguid.split("(")[1].split(")")[0]
	return "0x" + guid


def get_desc(Ca, Switch, devid):
	if Switch != None:
		desc = Switch.split("#")[1].split('"')[1]
	else:
		desc = Ca.split("#")[1].split('"')[1]

	type_desc = get_type_desc(devid)
	if type_desc != None:
		desc = desc + " " + type_desc

	return desc

def get_ports(Ca, Switch):
	if Switch != None:
		ports = Switch.split()[0]
	else:
		ports = Ca.split()[0]
	return ports

def get_port_num(Ca, Switch, port_desc):
	if Switch != None:
		portnum = '-'
	else:
		portnum = port_desc.split("[")[1].split("]")[0]
	return portnum

def comp_lid(a, b):
	lid_index = 5
	lid_a = int(a.split()[lid_index])
	lid_b = int(b.split()[lid_index])
	if lid_a >= lid_b: return -1
	return 1

def comp_type(a, b):
	type_index = 1
	type_a = str(a.split()[type_index])
	type_b = str(b.split()[type_index])

	if type_a.startswith("CA"):
		int_a = 3
	if type_b.startswith("CA"):
		int_b = 3

	if type_a.startswith("BridgeX"):
		int_a = 2
	if type_b.startswith("BridgeX"):
		int_b = 2

	if type_a.startswith("Switch"):
		int_a = 1
	if type_b.startswith("Switch"):
		int_b = 1

	if int_a >= int_b: return -1
	return 1

# main
def main():
	if os.geteuid() != 0:
		print "-E- you must be root to run this script."
		return 1

	elif len(sys.argv) > 2:
		print "USAGE:" + usage
		return 2
	else:
		cmd_flags = ''

	if len(sys.argv) == 2:
		port =  sys.argv[1]
		cmd_flags = "-P %s" % port

	cmd = "ibnetdiscover"
	cmd = cmd + " " + cmd_flags
	(rc,out) = run_cmd(cmd)
	if rc:
		print "-E- failed to run %s" % cmd
		return 3

	cnt = 0
	vendid=None
	devid=None
	sysimgguid=None
	switchguid=None
	caguid=None
	Switch=None
	Ca=None
	ports=[]
	out_list = []

	vprint("-V- found %d lines, %d chars" % (len(out.split("\n")), len(out)))
	for line in out.split("\n"):
		cnt = cnt + 1
		vprint("-V- process line (%d chars): %s" % (len(line), line))
		if line.startswith("vendid"):
			vendid=line.split('=')[1]
			devid=None
			sysimgguid=None
			switchguid=None
			caguid=None
			Switch=None
			Ca=None
			ports=[]
		elif line.startswith("devid"):
			devid=line.split('=')[1]
		elif line.startswith("sysimgguid"):
			sysimgguid=line.split('=')[1]
		elif line.startswith("switchguid"):
			switchguid=line.split('=')[1]
		elif line.startswith("caguid"):
			caguid=line.split('=')[1]
		elif line.startswith("Switch"):
			Switch=" ".join(line.split()[1:])
		elif line.startswith("Ca"):
			Ca=" ".join(line.split()[1:])
		elif line.startswith("["):
			ports += [line]
		else:
			vprint("-V- skipped")

		if cnt == 1:
			print "%-12s %-8s %-5s %-8s %-16s %-4s %s" % \
			      ("Vendor",   "Type",      "Ports",    "PortNum",   "GUID",      "LID",      "Description")
			print   12*"-"+' '+  8*"-"+' '+  5*"-"+' '+  8*"-"+' '+   16*"-"+' '+  4*"-"+' '+  11*"-"+ ''
		if (Ca != None or Switch != None) and len(line) == 0:
			for port in ports:
				type = get_type(Ca, Switch)
				vendor = get_vendor(vendid)
				lid = get_lid(Ca, Switch, port)
				guid = get_guid(Ca, Switch, port, switchguid)
				desc = get_desc(Ca, Switch, devid)
				portnum = get_port_num(Ca, Switch, port)
				ports = get_ports(Ca, Switch)
				vprint("-V- type (%s) vendor (%s) lid (%s)guid (%s) desc (%s) portnum (%s)" % \
				       (type, vendor, lid, guid, desc, portnum))
				out =  "%-12s %-8s %-5s %-8s %-16s %-4s %s" % \
				       (vendor, type, ports, portnum, guid, lid, desc)
				out_list += [out]
				if type == "Switch":
					break

	out_list.sort(comp_lid)
	out_list.sort(comp_type)
	print "\n".join(out_list)
	vprint("-V- done [rc %d]" % rc)
	return rc

if __name__ == '__main__':
	try:
		rc = main()
	except Exception, e:
		rc = 1
		print ""
		print "-E- Interrupted! %s" % str(e)
		print "-E- Abort."
	sys.exit(rc)

